Trước tiên, chúng ta hãy cùng cafedev giới thiệu mọi thứ về Strategy Pattern và phần code ví dụ chi tiết nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.
Như mọi khi, chúng ta sẽ học mô hình này bằng cách xác định một vấn đề và sử dụng mô hình chiến lược(strategy pattern) để giải quyết nó. Giả sử chúng ta đang xây dựng một trò chơi “Street Fighter”. Để đơn giản, giả sử rằng một nhân vật có thể có bốn động tác là đá, đấm, lăn và nhảy. Mỗi nhân vật đều có các động tác đấm và đá, nhưng lăn và nhảy là tùy chọn. Bạn sẽ mô hình lớp học của mình như thế nào? Giả sử ban đầu bạn sử dụng tính năng kế thừa và trừu tượng hóa các đặc điểm chung trong một lớp Fighter và để các nhân vật khác xếp lớp con cho lớp Fighter .
Lớp Fighter chúng ta sẽ có mặc định thực hiện các hành động bình thường. Bất kỳ nhân vật nào có động tác chuyên biệt đều có thể ghi đè hành động đó trong lớp con của nó. Sơ đồ lớp sẽ như sau:
Nội dung chính
1. Những vấn đề với thiết kế trên là gì?
Điều gì sẽ xảy ra nếu một nhân vật không thực hiện bước nhảy? Nó vẫn kế thừa hành vi nhảy từ lớp cha. Mặc dù bạn có thể ghi đè jump để không làm gì trong trường hợp đó nhưng bạn có thể phải làm như vậy cho nhiều lớp hiện có và quan tâm đến việc đó cho các lớp trong tương lai. Điều này cũng sẽ gây khó khăn cho việc bảo trì. Vì vậy, chúng ta không thể sử dụng kế thừa ở đây.
2. Về một Interface?
Hãy xem thiết kế sau:
Nó sạch hơn nhiều. Chúng ta đã thực hiện một số hành động (mà một số nhân vật có thể không thực hiện) trong lớp Fighter và tạo giao diện(Interface) cho chúng. Bằng cách đó, chỉ những nhân vật được cho là sẽ nhảy mới thực hiện JumpBehavior.
Những vấn đề với thiết kế trên là gì?
Vấn đề chính với thiết kế trên là sử dụng lại code. Vì không có triển khai mặc định của hành vi nhảy và cuộn, chúng tôi có thể có sự trùng lặp mã. Bạn có thể phải viết đi viết lại cùng một hành vi nhảy trong nhiều lớp con.
Làm thế nào chúng ta có thể tránh điều này?
Nếu chúng ta làm JumpBehavior và RollBehavior lớp thay vì giao diện(Interface)? Vậy thì chúng ta sẽ phải sử dụng đa kế thừa không được hỗ trợ trong nhiều ngôn ngữ do nhiều vấn đề liên quan đến nó.
Đây là mô hình chiến lược giải cứu chúng ta. Chúng ta sẽ tìm hiểu mô hình chiến lược là gì và sau đó áp dụng nó để giải quyết vấn đề của chúng ta.
3. Định nghĩa:
Wikipedia định nghĩa strategy pattern là:
“Trong lập trình máy tính, strategy pattern (còn được gọi là mẫu chính sách(policy pattern) ) là một mẫu thiết kế phần mềm cho phép chọn hành vi của một thuật toán trong thời gian chạy. Mô hình chiến lược
- Xác định một nhóm thuật toán,
- đóng gói từng thuật toán và
- làm cho các thuật toán có thể hoán đổi cho nhau trong gia đình đó. “
Sơ đồ lớp :
Ở đây chúng ta dựa vào thành phần thay vì kế thừa để sử dụng lại. Bối cảnh bao gồm một Chiến lược (Strategy). Thay vì thực hiện một hành vi, Context ủy thác nó cho Chiến lược(Strategy). Bối cảnh sẽ là lớp yêu cầu thay đổi hành vi. Chúng ta có thể thay đổi hành vi một cách linh hoạt. Chiến lược(Strategy) được thực hiện dưới dạng giao diện để chúng ta có thể thay đổi hành vi mà không ảnh hưởng đến bối cảnh của chúng ta.
Chúng ta sẽ hiểu rõ hơn về mô hình chiến lược(strategy pattern) khi chúng ta sẽ sử dụng nó để giải quyết vấn đề của mình.
4. Ưu điểm:
- Một thuật toán có thể được định nghĩa như một hệ thống phân cấp lớp và có thể được sử dụng thay thế cho nhau để thay đổi hành vi ứng dụng mà không thay đổi kiến trúc của nó.
- Bằng cách đóng gói riêng biệt thuật toán, các thuật toán mới tuân theo cùng một giao diện có thể dễ dàng được giới thiệu.
- Ứng dụng có thể chuyển đổi chiến lược tại thời điểm chạy.
- Chiến lược cho phép khách hàng chọn thuật toán cần thiết mà không cần sử dụng câu lệnh “switch” hoặc một loạt câu lệnh “if-else”.
- Cấu trúc dữ liệu được sử dụng để thực hiện thuật toán hoàn toàn được gói gọn trong các lớp Chiến lược(strategy). Do đó, việc thực hiện một thuật toán có thể được thay đổi mà không ảnh hưởng đến lớp Context.
5. Nhược điểm:
- Ứng dụng phải nhận thức được tất cả các chiến lược để chọn đúng chiến lược(strategy) cho tình huống phù hợp.
- Context và các lớp Strategy thường giao tiếp thông qua giao diện được chỉ định bởi lớp cơ sở Strategy trừu tượng. Lớp cơ sở chiến lược phải hiển thị giao diện cho tất cả các hành vi được yêu cầu, mà một số lớp Chiến lược cụ thể có thể không triển khai.
- Trong hầu hết các trường hợp, ứng dụng định cấu hình Context với đối tượng Strategy được yêu cầu. Do đó, ứng dụng cần tạo và duy trì hai đối tượng thay cho một.
Bài tiếp theo chúng ta sẽ triển khai Strategy Pattern trên code cụ thể.
Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.
Tài liệu từ cafedev:
- Full series tự học Design Pattern từ cơ bản tới nâng cao tại đây nha.
- Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây
Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:
Chào thân ái và quyết thắng!